package org.halk.export.crawler;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.io.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 *
 *
 * @author halk
 * @date 2025/9/27 11:15
 **/
@Slf4j
public class EegScannerStatistics {

    static String siteListUrl = "https://www.ccbd.tech/service/biims-clctdash/api/statistics/clctday/site?page=1&size=999&projectId=VER-1";
    static String nameUrl = "https://www.ccbd.tech/service/biims-clctdash/api/qc/mri/chiefExaminerSelect?modalityId=eeg";
    static String dataUrl = "https://www.ccbd.tech/service/biims-clctdash/api/qc/eeg/site/view?page=1&size=10000&filterAgeType=2&passCompareSign=2";
    static String excelPath = "D:/eeg.xlsx";
    static String serDataPath = "D:/eeg_scanner_statistics.dat";
    static String scannerNameExp = "scannerNameList";

    public static final String token = "Bearer 95e8c2b1-dcd1-4331-88e4-52268b828a0b";
    public static HashMap<String, String> headMap = new HashMap<>();
    static {
        headMap.put("Authorization", token);
    }

    // 创建固定大小为8的线程池
    static ExecutorService executorService = Executors.newFixedThreadPool(8);

    /**
     * 单纯以主试搜索，会漏掉一部分站点，改为站点主试一起搜索
     *
     * 先获取所有的站点，在获取站点对应的主试列表
     * @param args
     */
    public static void main(String[] args) {
        // 从文件读取
        ConcurrentHashMap<String, Map<String, BhvVO>> loadedMap = deserializeFromFile(serDataPath);
        filterEmpytMap(loadedMap);
        //获取主试
        List<SiteVO> siteVOList = getNameList();
        System.out.println("获取到站点的数量: " + siteVOList.size());

        siteVOList = siteVOList.stream().filter(x -> !loadedMap.containsKey(x.getSiteName())).collect(Collectors.toList());
        System.out.println("需要获取站点数量: " + siteVOList.size());

        ConcurrentHashMap<String, Map<String, BhvVO>> nameToMap = getNameSiteIdTaskNameMap(siteVOList);

        loadedMap.putAll(nameToMap);
        filterEmpytMap(loadedMap);
        // 序列化到文件
        serializeToFile(loadedMap, serDataPath);

        log.info("结束，新增 ： {}, 共计： {}", nameToMap.size(), loadedMap.size());

        exportExcel(loadedMap);

        System.exit(1);
    }

    private static void filterEmpytMap(ConcurrentHashMap<String, Map<String, BhvVO>> loadedMap) {
        //去除空的
        List<String> emptyKey = loadedMap.keySet().stream().filter(x -> loadedMap.get(x).isEmpty()).collect(Collectors.toList());
        for (String key : emptyKey) {
            loadedMap.remove(key);
        }
    }

    private static void exportExcel(ConcurrentHashMap<String, Map<String, BhvVO>> loadedMap) {
        log.info("开始导出数据");
        List<ExcelVO> excelVOList = new ArrayList<>();
        loadedMap.forEach((siteName, cannerNameTaskIdToVoMap) -> {
            cannerNameTaskIdToVoMap.forEach((siteId, vo) -> {
                excelVOList.add(new ExcelVO()
                        .setSiteName(siteName)
                        .setName(siteId.split(",")[0])
                        .setTaskName(siteId.split(",")[1])
                        .setClctNum(vo.getClctNum().toString())
                        .setQcNum(vo.getQcNum().toString())
                        .setRate(vo.getClctNum() == 0 ? "0" : NumberUtil.formatPercent(Double.valueOf(vo.getQcNum()) / vo.getClctNum(), 2)));
            });
        });

        excelVOList.sort(Comparator.comparing(ExcelVO::getSiteName).thenComparing(ExcelVO::getName).thenComparing(ExcelVO::getTaskName));

        // 导出文件
        File file = new File(excelPath);
        try (ExcelWriter excelWriter = EasyExcel.write(file).build()) {
            WriteSheet writeSheet;
            // 构建sheet对象
            writeSheet = EasyExcel.writerSheet("1").head(ExcelVO.class).build();
            // 写出sheet数据
            excelWriter.write(excelVOList, writeSheet);
            // 关流
            excelWriter.finish();
        } catch (Exception e) {
            // do something you want
        }

        log.info("导出数据完成");
    }

    /**
     * 将 ConcurrentHashMap 序列化到文件
     * @param map 要序列化的map
     * @param filePath 文件路径
     */
    private static void serializeToFile(ConcurrentHashMap<String, Map<String, BhvVO>> map, String filePath) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {
            oos.writeObject(map);
            System.out.println("数据已序列化到: " + filePath);
        } catch (IOException e) {
            System.err.println("序列化失败: " + e.getMessage());
            e.printStackTrace();
        }
    }


    /**
     * 从文件反序列化 ConcurrentHashMap
     * @param filePath 文件路径
     * @return 反序列化的map
     */
    private static ConcurrentHashMap<String, Map<String, BhvVO>> deserializeFromFile(String filePath) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {
            @SuppressWarnings("unchecked")
            ConcurrentHashMap<String, Map<String, BhvVO>> map =
                    (ConcurrentHashMap<String, Map<String, BhvVO>>) ois.readObject();
            System.out.println("数据已从文件读取: " + filePath);
            return map;
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("反序列化失败: " + e.getMessage());
            e.printStackTrace();
            return new ConcurrentHashMap<>();
        }
    }

    /**
     * 获取每个被试的数据
     * @param siteVOList
     * @return
     */
    private static ConcurrentHashMap<String, Map<String, BhvVO>> getNameSiteIdTaskNameMap(List<SiteVO> siteVOList) {
        //siteId, name,tasId,bhvvo
        ConcurrentHashMap<String, Map<String, BhvVO>> nameToMap = new ConcurrentHashMap<>();

        if (CollectionUtils.isEmpty(siteVOList)) {
            return nameToMap;
        }

        // 创建 CompletableFuture 列表
        // 等待所有任务完成
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(siteVOList.stream()
                .map(siteVO -> CompletableFuture.runAsync(() -> {
                    try {
                        Map<String, BhvVO> cannerNameTaskIdMap = getSiteIdTaskNameMap(siteVO);
                        nameToMap.put(siteVO.getSiteName(), cannerNameTaskIdMap);
                        System.out.println("站点: " + siteVO.getSiteName() + " 数量: " + cannerNameTaskIdMap.size());
                    } catch (Exception e) {
                        System.err.println("处理站点 " + siteVO + " 时发生错误: " + e.getMessage());
                        e.printStackTrace();
                    }
                }, executorService)).toArray(CompletableFuture[]::new));

        try {
            allTasks.join(); // 等待所有任务完成
        } catch (Exception e) {
            System.err.println("等待任务完成时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
        return nameToMap;
    }

    private static Map<String, BhvVO> getSiteIdTaskNameMap(SiteVO siteVO) {
        // 构建参数 map
        Map<String, Object> params = new HashMap<>();
        params.put("siteId", siteVO.getSiteId());

        //用于收集: scannerName,taskId -> BhvVO
        HashMap<String, BhvVO> scannerNameTaskIdToVoMap = new HashMap<>();
        for (String scannerName : siteVO.getScannerNameList()) {
            params.put("scannerName", scannerName);

            // 将参数拼接到基础 URL 上
            String fullUrl = URLUtil.buildQuery(params, null); // 第二个参数为编码字符集，默认 UTF-8
            fullUrl = dataUrl + "&" + fullUrl; // 若 bhvUrl 已含 '?' 则用 '&' 连接

            //获取主试的BHV数据
            HttpResponse execute = HttpRequest
                    .get(fullUrl)
                    .contentType("application/json")
                    .addHeaders(headMap)
                    .execute();
            String body = execute.body();
            JSON json = JSONUtil.parse(body);
            List<Object> contentList = (List<Object>) json.getByPath("content");

            if (contentList == null || contentList.isEmpty()) {
                System.out.println(body);
                continue;
            }
            for (Object content : contentList) {
                JSON contentJson = JSONUtil.parse(content);
                Object byPath = contentJson.getByPath("currentTaskCompleteCount");
                Integer clctNum = byPath == null ? 0 :  Integer.parseInt(byPath.toString());
                byPath = contentJson.getByPath("qcResultPassCount");
                Integer qcNum = byPath == null ? 0 :  Integer.parseInt(byPath.toString());
                String siteId = (String) contentJson.getByPath("siteName");
                String taskName = (String) contentJson.getByPath("taskName");

                String key = scannerName + "," + taskName;
                BhvVO bhvVO = scannerNameTaskIdToVoMap.getOrDefault(key, new BhvVO());
                bhvVO.setClctNum(clctNum + bhvVO.getClctNum());
                bhvVO.setQcNum(qcNum + bhvVO.getQcNum());
                scannerNameTaskIdToVoMap.put(key, bhvVO);
            }

            //            scannerNameTaskIdToVoMap.forEach((key, value) -> {
//                System.out.println(key + " -> " + value);
//            });
        }


        return scannerNameTaskIdToVoMap;
    }


    /**
     * 获取主试列表
     * 先获取站点，再获取站点对应主试列表
     *
     * @return
     */
    private static List<SiteVO> getNameList() {
        HttpResponse siteListResponse = HttpRequest
                .get(siteListUrl)
                .contentType("application/json")
                .addHeaders(headMap)
                .execute();
        if (!siteListResponse.isOk()) {
            throw new RuntimeException("获取站点列表失败: " + siteListResponse.body());
        }
        List<String> siteIdList = (List<String>) JSONUtil.parse(siteListResponse.body()).getByPath("siteId");
        List<String> siteNameList = (List<String>) JSONUtil.parse(siteListResponse.body()).getByPath("siteName");


        // 使用 CompletableFuture 并发执行
        List<CompletableFuture<SiteVO>> futures = siteIdList.stream()
                .filter(Objects::nonNull)
                .map(siteId -> CompletableFuture.supplyAsync(() -> buildSiteVo(siteId, siteNameList, siteIdList), executorService))
                .collect(Collectors.toList());

        // 等待所有任务完成并收集结果
        return futures.stream()
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private static SiteVO buildSiteVo(String siteId, List<String> siteNameList, List<String> siteIdList) {
        HttpResponse execute = HttpRequest
                .get(nameUrl + "&siteId=" + siteId)
                .contentType("application/json")
                .addHeaders(headMap)
                .execute();

        String body = execute.body();
        if (!execute.isOk()) {
            throw new RuntimeException("获取主试列表失败: " + body);
        }
        List<String> scannerNameList = (List<String>) JSONUtil.parse(body).getByPath(scannerNameExp);

        SiteVO siteVO = new SiteVO()
                .setSiteId(siteId)
                .setSiteName(siteNameList.get(siteIdList.indexOf(siteId)))
                .setScannerNameList(scannerNameList)
                .setScannerNameList(scannerNameList);
        log.info("获取站点{}对应的主试列表{}", siteVO.getSiteName(), siteVO.getScannerNameList());
        return siteVO;
    }


    @Data
    @Accessors(chain = true)
    static class BhvVO implements Serializable {
        private static final long serialVersionUID = 1L;
        private Integer clctNum = 0;
        private Integer qcNum = 0;
    }

    @Data
    @Accessors(chain = true)
    static class SiteVO implements Serializable {
        private static final long serialVersionUID = 1L;
        private String siteId;
        private String siteName;
        private List<String> scannerNameList;
    }
}
